1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.collect;
18
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.base.Function;
21 import com.google.common.collect.Table.Cell;
22 import com.google.common.collect.testing.MapInterfaceTest;
23 import com.google.common.collect.testing.SampleElements;
24 import com.google.common.collect.testing.TestSetGenerator;
25 import com.google.common.collect.testing.features.CollectionFeature;
26 import com.google.common.collect.testing.features.CollectionSize;
27 import com.google.common.collect.testing.features.Feature;
28
29 import junit.framework.TestCase;
30
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.SortedMap;
35
36
37
38
39
40
41
42 @GwtCompatible(emulated = true)
43 public class TableCollectionTest extends TestCase {
44
45 private static final Feature<?>[] COLLECTION_FEATURES = {
46 CollectionSize.ANY,
47 CollectionFeature.ALLOWS_NULL_QUERIES
48 };
49
50 private static final Feature<?>[] COLLECTION_FEATURES_ORDER = {
51 CollectionSize.ANY,
52 CollectionFeature.KNOWN_ORDER,
53 CollectionFeature.ALLOWS_NULL_QUERIES
54 };
55
56 private static final Feature<?>[] COLLECTION_FEATURES_REMOVE = {
57 CollectionSize.ANY,
58 CollectionFeature.SUPPORTS_REMOVE,
59 CollectionFeature.ALLOWS_NULL_QUERIES
60 };
61
62 private static final Feature<?>[] COLLECTION_FEATURES_REMOVE_ORDER = {
63 CollectionSize.ANY,
64 CollectionFeature.KNOWN_ORDER,
65 CollectionFeature.SUPPORTS_REMOVE,
66 CollectionFeature.ALLOWS_NULL_QUERIES
67 };
68
69 private static void populateForRowKeySet(
70 Table<String, Integer, Character> table, String[] elements) {
71 for (String row : elements) {
72 table.put(row, 1, 'a');
73 table.put(row, 2, 'b');
74 }
75 }
76
77 private static void populateForColumnKeySet(
78 Table<Integer, String, Character> table, String[] elements) {
79 for (String column : elements) {
80 table.put(1, column, 'a');
81 table.put(2, column, 'b');
82 }
83 }
84
85 private static void populateForValues(
86 Table<Integer, Character, String> table, String[] elements) {
87 for (int i = 0; i < elements.length; i++) {
88 table.put(i, 'a', elements[i]);
89 }
90 }
91
92 private static abstract class TestCellSetGenerator
93 implements TestSetGenerator<Cell<String, Integer, Character>> {
94 @Override
95 public SampleElements<Cell<String, Integer, Character>> samples() {
96 return new SampleElements<Cell<String, Integer, Character>>(
97 Tables.immutableCell("bar", 1, 'a'),
98 Tables.immutableCell("bar", 2, 'b'),
99 Tables.immutableCell("foo", 3, 'c'),
100 Tables.immutableCell("bar", 1, 'b'),
101 Tables.immutableCell("cat", 2, 'b'));
102 }
103
104 @Override
105 public Set<Cell<String, Integer, Character>> create(
106 Object... elements) {
107 Table<String, Integer, Character> table = createTable();
108 for (Object element : elements) {
109 @SuppressWarnings("unchecked")
110 Cell<String, Integer, Character> cell
111 = (Cell<String, Integer, Character>) element;
112 table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
113 }
114 return table.cellSet();
115 }
116
117 abstract Table<String, Integer, Character> createTable();
118
119 @Override
120 @SuppressWarnings("unchecked")
121 public Cell<String, Integer, Character>[] createArray(int length) {
122 return (Cell<String, Integer, Character>[]) new Cell<?, ?, ?>[length];
123 }
124
125 @Override
126 public List<Cell<String, Integer, Character>> order(
127 List<Cell<String, Integer, Character>> insertionOrder) {
128 return insertionOrder;
129 }
130 }
131
132 private static abstract class MapTests
133 extends MapInterfaceTest<String, Integer> {
134
135 MapTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
136 boolean supportsClear, boolean supportsIteratorRemove) {
137 super(false, allowsNullValues, supportsPut, supportsRemove, supportsClear,
138 supportsIteratorRemove);
139 }
140
141 @Override protected String getKeyNotInPopulatedMap() {
142 return "four";
143 }
144
145 @Override protected Integer getValueNotInPopulatedMap() {
146 return 4;
147 }
148 }
149
150 private static abstract class RowTests extends MapTests {
151 RowTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
152 boolean supportsClear, boolean supportsIteratorRemove) {
153 super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
154 supportsIteratorRemove);
155 }
156
157 abstract Table<Character, String, Integer> makeTable();
158
159 @Override protected Map<String, Integer> makeEmptyMap() {
160 return makeTable().row('a');
161 }
162
163 @Override protected Map<String, Integer> makePopulatedMap() {
164 Table<Character, String, Integer> table = makeTable();
165 table.put('a', "one", 1);
166 table.put('a', "two", 2);
167 table.put('a', "three", 3);
168 table.put('b', "four", 4);
169 return table.row('a');
170 }
171 }
172
173 public static class HashRowTests extends RowTests {
174 public HashRowTests() {
175 super(false, true, true, true, true);
176 }
177
178 @Override Table<Character, String, Integer> makeTable() {
179 return HashBasedTable.create();
180 }
181 }
182
183 public static class TreeRowTests extends RowTests {
184 public TreeRowTests() {
185 super(false, true, true, true, true);
186 }
187
188 @Override Table<Character, String, Integer> makeTable() {
189 return TreeBasedTable.create();
190 }
191 }
192
193 public static class TransposeRowTests extends RowTests {
194 public TransposeRowTests() {
195 super(false, true, true, true, false);
196 }
197
198 @Override Table<Character, String, Integer> makeTable() {
199 Table<String, Character, Integer> original = TreeBasedTable.create();
200 return Tables.transpose(original);
201 }
202 }
203
204 private static final Function<Integer, Integer> DIVIDE_BY_2
205 = new Function<Integer, Integer>() {
206 @Override public Integer apply(Integer input) {
207 return (input == null) ? null : input / 2;
208 }
209 };
210
211 public static class TransformValueRowTests extends RowTests {
212 public TransformValueRowTests() {
213 super(false, false, true, true, true);
214 }
215
216 @Override Table<Character, String, Integer> makeTable() {
217 Table<Character, String, Integer> table = HashBasedTable.create();
218 return Tables.transformValues(table, DIVIDE_BY_2);
219 }
220
221 @Override protected Map<String, Integer> makePopulatedMap() {
222 Table<Character, String, Integer> table = HashBasedTable.create();
223 table.put('a', "one", 2);
224 table.put('a', "two", 4);
225 table.put('a', "three", 6);
226 table.put('b', "four", 8);
227 return Tables.transformValues(table, DIVIDE_BY_2).row('a');
228 }
229 }
230
231 public static class UnmodifiableHashRowTests extends RowTests {
232 public UnmodifiableHashRowTests() {
233 super(false, false, false, false, false);
234 }
235
236 @Override Table<Character, String, Integer> makeTable() {
237 Table<Character, String, Integer> table = HashBasedTable.create();
238 return Tables.unmodifiableTable(table);
239 }
240
241 @Override protected Map<String, Integer> makePopulatedMap() {
242 Table<Character, String, Integer> table = HashBasedTable.create();
243 table.put('a', "one", 1);
244 table.put('a', "two", 2);
245 table.put('a', "three", 3);
246 table.put('b', "four", 4);
247 return Tables.unmodifiableTable(table).row('a');
248 }
249 }
250
251 public static class UnmodifiableTreeRowTests extends RowTests {
252 public UnmodifiableTreeRowTests() {
253 super(false, false, false, false, false);
254 }
255
256 @Override Table<Character, String, Integer> makeTable() {
257 RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
258 return Tables.unmodifiableRowSortedTable(table);
259 }
260
261 @Override protected Map<String, Integer> makePopulatedMap() {
262 RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
263 table.put('a', "one", 1);
264 table.put('a', "two", 2);
265 table.put('a', "three", 3);
266 table.put('b', "four", 4);
267 return Tables.unmodifiableRowSortedTable(table).row('a');
268 }
269 }
270
271 private static abstract class ColumnTests extends MapTests {
272 ColumnTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
273 boolean supportsClear, boolean supportsIteratorRemove) {
274 super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
275 supportsIteratorRemove);
276 }
277
278 abstract Table<String, Character, Integer> makeTable();
279
280 @Override protected Map<String, Integer> makeEmptyMap() {
281 return makeTable().column('a');
282 }
283
284 @Override protected Map<String, Integer> makePopulatedMap() {
285 Table<String, Character, Integer> table = makeTable();
286 table.put("one", 'a', 1);
287 table.put("two", 'a', 2);
288 table.put("three", 'a', 3);
289 table.put("four", 'b', 4);
290 return table.column('a');
291 }
292 }
293
294 public static class HashColumnTests extends ColumnTests {
295 public HashColumnTests() {
296 super(false, true, true, true, false);
297 }
298
299 @Override Table<String, Character, Integer> makeTable() {
300 return HashBasedTable.create();
301 }
302 }
303
304 public static class TreeColumnTests extends ColumnTests {
305 public TreeColumnTests() {
306 super(false, true, true, true, false);
307 }
308
309 @Override Table<String, Character, Integer> makeTable() {
310 return TreeBasedTable.create();
311 }
312 }
313
314 public static class TransposeColumnTests extends ColumnTests {
315 public TransposeColumnTests() {
316 super(false, true, true, true, true);
317 }
318
319 @Override Table<String, Character, Integer> makeTable() {
320 Table<Character, String, Integer> original = TreeBasedTable.create();
321 return Tables.transpose(original);
322 }
323 }
324
325 public static class TransformValueColumnTests extends ColumnTests {
326 public TransformValueColumnTests() {
327 super(false, false, true, true, false);
328 }
329
330 @Override Table<String, Character, Integer> makeTable() {
331 Table<String, Character, Integer> table = HashBasedTable.create();
332 return Tables.transformValues(table, DIVIDE_BY_2);
333 }
334
335 @Override protected Map<String, Integer> makePopulatedMap() {
336 Table<String, Character, Integer> table = HashBasedTable.create();
337 table.put("one", 'a', 1);
338 table.put("two", 'a', 2);
339 table.put("three", 'a', 3);
340 table.put("four", 'b', 4);
341 return Tables.transformValues(table, DIVIDE_BY_2).column('a');
342 }
343 }
344
345 public static class UnmodifiableHashColumnTests extends ColumnTests {
346 public UnmodifiableHashColumnTests() {
347 super(false, false, false, false, false);
348 }
349
350 @Override Table<String, Character, Integer> makeTable() {
351 Table<String, Character, Integer> table = HashBasedTable.create();
352 return Tables.unmodifiableTable(table);
353 }
354
355 @Override protected Map<String, Integer> makePopulatedMap() {
356 Table<String, Character, Integer> table = HashBasedTable.create();
357 table.put("one", 'a', 1);
358 table.put("two", 'a', 2);
359 table.put("three", 'a', 3);
360 table.put("four", 'b', 4);
361 return Tables.unmodifiableTable(table).column('a');
362 }
363 }
364
365 public static class UnmodifiableTreeColumnTests extends ColumnTests {
366 public UnmodifiableTreeColumnTests() {
367 super(false, false, false, false, false);
368 }
369
370 @Override Table<String, Character, Integer> makeTable() {
371 RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
372 return Tables.unmodifiableRowSortedTable(table);
373 }
374
375 @Override protected Map<String, Integer> makePopulatedMap() {
376 RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
377 table.put("one", 'a', 1);
378 table.put("two", 'a', 2);
379 table.put("three", 'a', 3);
380 table.put("four", 'b', 4);
381 return Tables.unmodifiableRowSortedTable(table).column('a');
382 }
383 }
384
385 private static abstract class MapMapTests
386 extends MapInterfaceTest<String, Map<Integer, Character>> {
387
388 MapMapTests(boolean allowsNullValues, boolean supportsRemove,
389 boolean supportsClear, boolean supportsIteratorRemove) {
390 super(false, allowsNullValues, false, supportsRemove, supportsClear,
391 supportsIteratorRemove);
392 }
393
394 @Override protected String getKeyNotInPopulatedMap() {
395 return "cat";
396 }
397
398 @Override protected Map<Integer, Character> getValueNotInPopulatedMap() {
399 return ImmutableMap.of();
400 }
401
402
403
404
405
406
407
408
409 @Override public void testRemove() {
410 final Map<String, Map<Integer, Character>> map;
411 final String keyToRemove;
412 try {
413 map = makePopulatedMap();
414 } catch (UnsupportedOperationException e) {
415 return;
416 }
417 keyToRemove = map.keySet().iterator().next();
418 if (supportsRemove) {
419 int initialSize = map.size();
420 map.get(keyToRemove);
421 map.remove(keyToRemove);
422
423
424 assertFalse(map.containsKey(keyToRemove));
425 assertEquals(initialSize - 1, map.size());
426 } else {
427 try {
428 map.remove(keyToRemove);
429 fail("Expected UnsupportedOperationException.");
430 } catch (UnsupportedOperationException e) {
431
432 }
433 }
434 assertInvariants(map);
435 }
436 }
437
438 private static abstract class RowMapTests extends MapMapTests {
439 RowMapTests(boolean allowsNullValues, boolean supportsRemove,
440 boolean supportsClear, boolean supportsIteratorRemove) {
441 super(allowsNullValues, supportsRemove, supportsClear,
442 supportsIteratorRemove);
443 }
444
445 abstract Table<String, Integer, Character> makeTable();
446
447 @Override protected Map<String, Map<Integer, Character>>
448 makePopulatedMap() {
449 Table<String, Integer, Character> table = makeTable();
450 populateTable(table);
451 return table.rowMap();
452 }
453
454 void populateTable(Table<String, Integer, Character> table) {
455 table.put("foo", 1, 'a');
456 table.put("bar", 1, 'b');
457 table.put("foo", 3, 'c');
458 }
459
460 @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
461 return makeTable().rowMap();
462 }
463 }
464
465 public static class HashRowMapTests extends RowMapTests {
466 public HashRowMapTests() {
467 super(false, true, true, true);
468 }
469
470 @Override Table<String, Integer, Character> makeTable() {
471 return HashBasedTable.create();
472 }
473 }
474
475 public static class TreeRowMapTests extends RowMapTests {
476 public TreeRowMapTests() {
477 super(false, true, true, true);
478 }
479
480 @Override Table<String, Integer, Character> makeTable() {
481 return TreeBasedTable.create();
482 }
483 }
484
485 public static class TreeRowMapHeadMapTests extends RowMapTests {
486 public TreeRowMapHeadMapTests() {
487 super(false, true, true, true);
488 }
489
490 @Override TreeBasedTable<String, Integer, Character> makeTable() {
491 TreeBasedTable<String, Integer, Character> table =
492 TreeBasedTable.create();
493 table.put("z", 1, 'a');
494 return table;
495 }
496
497 @Override protected Map<String, Map<Integer, Character>>
498 makePopulatedMap() {
499 TreeBasedTable<String, Integer, Character> table = makeTable();
500 populateTable(table);
501 return table.rowMap().headMap("x");
502 }
503
504 @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
505 return makeTable().rowMap().headMap("x");
506 }
507
508 @Override protected String getKeyNotInPopulatedMap() {
509 return "z";
510 }
511 }
512
513 public static class TreeRowMapTailMapTests extends RowMapTests {
514 public TreeRowMapTailMapTests() {
515 super(false, true, true, true);
516 }
517
518 @Override TreeBasedTable<String, Integer, Character> makeTable() {
519 TreeBasedTable<String, Integer, Character> table =
520 TreeBasedTable.create();
521 table.put("a", 1, 'a');
522 return table;
523 }
524
525 @Override protected Map<String, Map<Integer, Character>>
526 makePopulatedMap() {
527 TreeBasedTable<String, Integer, Character> table = makeTable();
528 populateTable(table);
529 return table.rowMap().tailMap("b");
530 }
531
532 @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
533 return makeTable().rowMap().tailMap("b");
534 }
535
536 @Override protected String getKeyNotInPopulatedMap() {
537 return "a";
538 }
539 }
540
541 public static class TreeRowMapSubMapTests extends RowMapTests {
542 public TreeRowMapSubMapTests() {
543 super(false, true, true, true);
544 }
545
546 @Override TreeBasedTable<String, Integer, Character> makeTable() {
547 TreeBasedTable<String, Integer, Character> table =
548 TreeBasedTable.create();
549 table.put("a", 1, 'a');
550 table.put("z", 1, 'a');
551 return table;
552 }
553
554 @Override protected Map<String, Map<Integer, Character>>
555 makePopulatedMap() {
556 TreeBasedTable<String, Integer, Character> table = makeTable();
557 populateTable(table);
558 return table.rowMap().subMap("b", "x");
559 }
560
561 @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
562 return makeTable().rowMap().subMap("b", "x");
563 }
564
565 @Override protected String getKeyNotInPopulatedMap() {
566 return "z";
567 }
568 }
569
570 private static final Function<String, Character> FIRST_CHARACTER =
571 new Function<String, Character>() {
572 @Override
573 public Character apply(String input) {
574 return input == null ? null : input.charAt(0);
575 }
576 };
577
578 public static class TransformValueRowMapTests extends RowMapTests {
579 public TransformValueRowMapTests() {
580 super(false, true, true, true);
581 }
582
583 @Override Table<String, Integer, Character> makeTable() {
584 Table<String, Integer, String> original = HashBasedTable.create();
585 return Tables.transformValues(original, FIRST_CHARACTER);
586 }
587
588 @Override
589 protected Map<String, Map<Integer, Character>> makePopulatedMap() {
590 Table<String, Integer, String> table = HashBasedTable.create();
591 table.put("foo", 1, "apple");
592 table.put("bar", 1, "banana");
593 table.put("foo", 3, "cat");
594 return Tables.transformValues(table, FIRST_CHARACTER).rowMap();
595 }
596 }
597
598 public static class UnmodifiableHashRowMapTests extends RowMapTests {
599 public UnmodifiableHashRowMapTests() {
600 super(false, false, false, false);
601 }
602
603 @Override Table<String, Integer, Character> makeTable() {
604 Table<String, Integer, Character> original = HashBasedTable.create();
605 return Tables.unmodifiableTable(original);
606 }
607
608 @Override
609 protected Map<String, Map<Integer, Character>> makePopulatedMap() {
610 Table<String, Integer, Character> table = HashBasedTable.create();
611 table.put("foo", 1, 'a');
612 table.put("bar", 1, 'b');
613 table.put("foo", 3, 'c');
614 return Tables.unmodifiableTable(table).rowMap();
615 }
616 }
617
618 public static class UnmodifiableTreeRowMapTests extends RowMapTests {
619 public UnmodifiableTreeRowMapTests() {
620 super(false, false, false, false);
621 }
622
623 @Override RowSortedTable<String, Integer, Character> makeTable() {
624 RowSortedTable<String, Integer, Character> original = TreeBasedTable.create();
625 return Tables.unmodifiableRowSortedTable(original);
626 }
627
628 @Override
629 protected SortedMap<String, Map<Integer, Character>> makePopulatedMap() {
630 RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
631 table.put("foo", 1, 'a');
632 table.put("bar", 1, 'b');
633 table.put("foo", 3, 'c');
634 return Tables.unmodifiableRowSortedTable(table).rowMap();
635 }
636 }
637
638 private static abstract class ColumnMapTests extends MapMapTests {
639 ColumnMapTests(boolean allowsNullValues, boolean supportsRemove,
640 boolean supportsClear, boolean supportsIteratorRemove) {
641 super(allowsNullValues, supportsRemove, supportsClear,
642 supportsIteratorRemove);
643 }
644
645 abstract Table<Integer, String, Character> makeTable();
646
647 @Override protected Map<String, Map<Integer, Character>>
648 makePopulatedMap() {
649 Table<Integer, String, Character> table = makeTable();
650 table.put(1, "foo", 'a');
651 table.put(1, "bar", 'b');
652 table.put(3, "foo", 'c');
653 return table.columnMap();
654 }
655
656 @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
657 return makeTable().columnMap();
658 }
659 }
660
661 public static class HashColumnMapTests extends ColumnMapTests {
662 public HashColumnMapTests() {
663 super(false, true, true, false);
664 }
665
666 @Override Table<Integer, String, Character> makeTable() {
667 return HashBasedTable.create();
668 }
669 }
670
671 public static class TreeColumnMapTests extends ColumnMapTests {
672 public TreeColumnMapTests() {
673 super(false, true, true, false);
674 }
675
676 @Override Table<Integer, String, Character> makeTable() {
677 return TreeBasedTable.create();
678 }
679 }
680
681 public static class TransformValueColumnMapTests extends ColumnMapTests {
682 public TransformValueColumnMapTests() {
683 super(false, true, true, false);
684 }
685
686 @Override Table<Integer, String, Character> makeTable() {
687 Table<Integer, String, String> original = HashBasedTable.create();
688 return Tables.transformValues(original, FIRST_CHARACTER);
689 }
690
691 @Override
692 protected Map<String, Map<Integer, Character>> makePopulatedMap() {
693 Table<Integer, String, String> table = HashBasedTable.create();
694 table.put(1, "foo", "apple");
695 table.put(1, "bar", "banana");
696 table.put(3, "foo", "cat");
697 return Tables.transformValues(table, FIRST_CHARACTER).columnMap();
698 }
699 }
700
701 public static class UnmodifiableHashColumnMapTests extends ColumnMapTests {
702 public UnmodifiableHashColumnMapTests() {
703 super(false, false, false, false);
704 }
705
706 @Override Table<Integer, String, Character> makeTable() {
707 Table<Integer, String, Character> original = HashBasedTable.create();
708 return Tables.unmodifiableTable(original);
709 }
710
711 @Override
712 protected Map<String, Map<Integer, Character>> makePopulatedMap() {
713 Table<Integer, String, Character> table = HashBasedTable.create();
714 table.put(1, "foo", 'a');
715 table.put(1, "bar", 'b');
716 table.put(3, "foo", 'c');
717 return Tables.unmodifiableTable(table).columnMap();
718 }
719 }
720
721 public static class UnmodifiableTreeColumnMapTests extends ColumnMapTests {
722 public UnmodifiableTreeColumnMapTests() {
723 super(false, false, false, false);
724 }
725
726 @Override Table<Integer, String, Character> makeTable() {
727 RowSortedTable<Integer, String, Character> original = TreeBasedTable.create();
728 return Tables.unmodifiableRowSortedTable(original);
729 }
730
731 @Override
732 protected Map<String, Map<Integer, Character>> makePopulatedMap() {
733 RowSortedTable<Integer, String, Character> table = TreeBasedTable.create();
734 table.put(1, "foo", 'a');
735 table.put(1, "bar", 'b');
736 table.put(3, "foo", 'c');
737 return Tables.unmodifiableRowSortedTable(table).columnMap();
738 }
739 }
740 }
741